﻿#define DO_DELETE

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using ENG.EngStartQueueLib;
using System.IO;
using Microsoft.Win32;
using ENG.EngStartQueueLib.ShellLink;
using System.Diagnostics;

namespace ENG.EngStartQueue
{
  public partial class FrmManager : Form
  {

    private const string REG_PATH = Program.REG_PATH;

    private List<Reference> StartUpLinks = new List<Reference>();
    private List<Reference> HKCULinks = new List<Reference>();
    private List<Reference> HKLMLinks = new List<Reference>();

    private QueueSett q = new QueueSett();

    public FrmManager()
    {
      InitializeComponent();
    }

    private void FrmMain_Load(object sender, EventArgs e)
    {
      try
      {
        q = QueueSett.Load(Program.QueueFile);
      }
      catch (FileNotFoundException)
      {
        if (q == null)
          q = new QueueSett();
      }
      catch (Exception)
      {
        throw;
      }

      FillLists();

      execInfoCollectionBindingSource.DataSource = q.Items ;

      resetIsChanged();
      CheckEnableDisable();
    }

    private void FillLists()
    {
      StartUpLinks.Clear();
      HKCULinks.Clear();
      HKLMLinks.Clear();
      string[] files;
      string path;

      path = Program.StartupFolder;
      if (System.IO.Directory.Exists (path))
      {
        files = System.IO.Directory.GetFiles(path, "*.lnk");
        foreach (var fItem in files)
        {
          LinkReference lr = LinkReference.Create(fItem);
          StartUpLinks.Add(lr);
        } // foreach (var fItem in files)
        lvwAfterStartUp.Set(StartUpLinks);
      }
      else
      {
        MessageBox.Show ("Startup folder " + path + " does not exist. Check your settings of startup folder in the options menu.",
          Application.ProductName,  MessageBoxButtons.OK, MessageBoxIcon.Error);
      }

      files = RegistryAPI.GetKey ( 
        "HKCU\\" + REG_PATH).GetValueNames();
      foreach (var fItem in files)
      {
        RegistryReference rr = RegistryReference.Create(RegistryAPI.eRoot.HKCU, REG_PATH, fItem);
        HKCULinks.Add(rr);
      } // foreach (var fItem in files)
      lvwRegHKCU.Set(HKCULinks);

      files = RegistryAPI.GetKey(
          "HKLM\\" + REG_PATH).GetValueNames();
      foreach (var fItem in files)
      {
        RegistryReference rr = RegistryReference.Create(RegistryAPI.eRoot.HKLM, REG_PATH, fItem);
        HKLMLinks.Add(rr);
      } // foreach (var fItem in files)
      lvwRegHKLM.Set(HKLMLinks);
    }

    private void AddFiles(List<Reference> lst, ListView lvw)
    {
      lvw.Items.Clear();

      foreach (var fItem in lst)
      {
        ListViewItem ni = new ListViewItem();
        ni.Text = fItem.FileName;
        ni.ToolTipText = fItem.FullPath;
        lvw.Tag = fItem;
        lvw.Items.Add(ni);
      } // foreach (var fItem in files)
    }

    private void btnAdd_Click(object sender, EventArgs e)
    {
      FrmNewExecInfo f = new FrmNewExecInfo();
      f.Value = new ExecInfo();

      f.ShowDialog();

      if (f.DialogResult != DialogResult.OK)
        return;

      q.Items.Add(f.Value);

      q.Store(Program.QueueFile);

      execInfoCollectionBindingSource.ResetBindings(false);
    }

    private void btnOptions_Click(object sender, EventArgs e)
    {
      new FrmOptions().ShowDialog();
    }

    private void btnRemove_Click(object sender, EventArgs e)
    {
      //if (lstItems.SelectedIndex == -1) return;
      if (grd.CurrentCell == null) return;

      if (DialogResult.Yes ==
        MessageBox.Show("Are you absolutely sure about deleting selected item? This cannot be undone!",
        "Delete item?", MessageBoxButtons.YesNo, MessageBoxIcon.Question))
      {
        q.Items.RemoveAt(grd.CurrentCell.RowIndex);

        execInfoCollectionBindingSource.ResetBindings(false);

        isChanged();
      }
    }

    private void btnInsert_Click(object sender, EventArgs e)
    {
      CtrRefListView lvw = (CtrRefListView)tabControl1.SelectedTab.Controls[0];

      if (Program.LibSett.AutoSaveOnInsert)
        btnSave_Click(null, null);

      foreach (Reference fItem in lvw.GetSelectedItems())
      {
        InsertItem(fItem);
      } // foreach (var fItem in lvw.SelectedItems)

      FillLists();

      execInfoCollectionBindingSource.ResetBindings(false);
      isChanged();

      if (Program.LibSett.AutoSaveOnInsert)
        btnSave_Click(null, null);
    }

    private void InsertItem(Reference fItem)
    {
      ExecInfo ei = CloneToExecInfo(fItem);

      q.Items.Add(ei);
    }

    private ExecInfo CloneToExecInfo(Reference fItem)
    {
      ExecInfo ret = null; ;

      if (fItem is LinkReference)
        ret = CloneShellLinkToExecInfo((LinkReference) fItem, true);
      else
        ret = CloneRegItemToExecInfo((RegistryReference) fItem, true);

      return ret;
    }

    private ExecInfo CloneRegItemToExecInfo(RegistryReference link, bool removeOrigin)
    {
      ExecInfo ret = null;

      try
      {
        ret = new ExecInfo(link);
      }
      catch (Exception ex)
      {
        MessageBox.Show("Failed to achieve required information from value name " + link.ValueName + 
          " in registry key " + link.Root +
            "\\" + link.Key + ". Reason: " + ex.Message +
            " This item is not added into list.",
            Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
        ret = null;
      }

#if DO_DELETE
      if (removeOrigin && ret != null)
      {
        try
        {
          RegistryKey regKey = RegistryAPI.GetRootKey(link.Root);
          regKey = regKey.CreateSubKey(link.Key);
          regKey.DeleteValue(link.ValueName);
        }
        catch (Exception ex)
        {
          MessageBox.Show("Failed to remove value name " + link.ValueName + " from registry key " + link.Root +
            "\\" + link.Key + ". Reason: " + ex.Message + 
            " This item is not removed from the source and therefore not added into list.",
            Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
          ret = null;
        }
      }
#endif

      return ret;
    }

    private ExecInfo CloneShellLinkToExecInfo(LinkReference link, bool removeOrigin)
    {

      ExecInfo ret = null;
      try
      {
        ret = new ExecInfo(link);
      }
      catch (Exception ex)
      {
        MessageBox.Show("Failed to achieve required information from link file " + link.Source + ". Reason: " + ex.Message +
            " This item is not added into list.",
            Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
        ret = null;
      }

#if DO_DELETE
      if (removeOrigin && ret != null)
      {
        try
        {
          System.IO.File.Delete(link.Source);
        }
        catch (Exception ex)
        {
          MessageBox.Show("Failed to delete link file " + link.Source + ". Reason: " + ex.Message +
            " This item is not removed from the source and therefore not added into list.",
            Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
          ret = null;
        }
      }
#endif

      return ret;

    }

    private void grd_RowEnter(object sender, DataGridViewCellEventArgs e)
    {
      int rowIndex = e.RowIndex;

      if (rowIndex >= q.Items.Count)
        ctrExec.Value = null;
      else
        ctrExec.Value = q.Items[rowIndex];
    }

    private void btnSubmitChanges_Click(object sender, EventArgs e)
    {
      int rowIndex = grd.CurrentCell.RowIndex;

      object o = ctrExec.Value;

      execInfoCollectionBindingSource.ResetBindings(false);

      isChanged();
    }

    private void btnUp_Click(object sender, EventArgs e)
    {
      int b = grd.CurrentCell.RowIndex;
      int a = grd.CurrentCell.RowIndex - 1;

      if (a < 0) return;

      ExecInfo item = q.Items[b];
      q.Items.RemoveAt(b);
      q.Items.Insert(a, item);

      execInfoCollectionBindingSource.ResetBindings(false);

      grd.Rows[a].Cells[1].Selected = true;

      isChanged();
    }

    private void btnDown_Click(object sender, EventArgs e)
    {
      int a = grd.CurrentCell.RowIndex;
      int b = grd.CurrentCell.RowIndex+1;

      if (b >= q.Items.Count) return;

      ExecInfo item = q.Items[b];
      q.Items.RemoveAt(b);
      q.Items.Insert(a, item);

      execInfoCollectionBindingSource.ResetBindings(false);

      grd.Rows[b].Cells[1].Selected = true;

      isChanged();
    }

    private void btnSave_Click(object sender, EventArgs e)
    {
      q.Store(Program.QueueFile);

      resetIsChanged();
    }

    private void resetIsChanged()
    {
      tmrSave.Enabled = false;
      btnSave.Image = Properties.Resources.ico_Save;
    }

    private void isChanged()
    {
      tmrSave.Enabled = true;
    }

    private void btnConfirmAndNext_Click(object sender, EventArgs e)
    {
      btnSubmitChanges_Click(null, null);

      if (grd.CurrentCell.RowIndex < q.Items.Count)
      {
        grd.Rows[grd.CurrentCell.RowIndex + 1].Cells[1].Selected = true;
        ctrExec.Focus();
      }
    }

    private void btnReload_Click(object sender, EventArgs e)
    {
      q = QueueSett.Load(Program.QueueFile);

      execInfoCollectionBindingSource.DataSource = q.Items;

      resetIsChanged();
    }

    private void CheckEnableDisable()
    {
      bool isMeThere = false;

      foreach (var fItem in HKCULinks)
      {
        if (fItem.FullPath.ToLower() == Application.ExecutablePath.ToLower())
        {
          isMeThere = true;
          break;
        }
      } // foreach (var fItem in HKCULinks)

      if (isMeThere)
      {
        btnEnDis.Text = "! &Disable !";
        btnEnDis.Image = Properties.Resources.ico_Disable;
        btnEnDis.Tag = "D";
      }
      else
      {
        btnEnDis.Text = "! &Enable !";
        btnEnDis.Tag = "E";
        btnEnDis.Image = Properties.Resources.ico_Enable;
      }
    }

    private void btnEnDis_Click(object sender, EventArgs e)
    {
      ExecInfo ei = null;

      string p = "HKCU\\" + REG_PATH + "\\EngStartQueue";

      if ((string)btnEnDis.Tag == "E")
      {
        ei = new ExecInfo();
        ei.Arguments = "-e";
        ei.ExplicitDelay = 0;
        ei.FullPath = Application.ExecutablePath;
        ei.IsActive = true;
        ei.Priority = System.Diagnostics.ProcessPriorityClass.Normal;
        ei.ExplicitlySetSource(p);
        ei.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
        ei.WorkingDirectory = "";

        RestoreRegistry(ei);
      }
      else if ((string)btnEnDis.Tag == "D")
      {
        RegistryReference rr = RegistryReference.Create(p);
        ei = CloneRegItemToExecInfo(rr, true);
      }

      FillLists();

      CheckEnableDisable();
    }

    private bool RestoreRegistry(ExecInfo ei)
    {
      bool ret = false;

      try
      {
        RegistryAPI.Insert(ei.Source, "\"" + ei.FullPath + "\" " + ei.Arguments);
        ret = true;
      }
      catch (Exception ex)
      {
        MessageBox.Show(
          "Restoring value into registry " + ei.Source + " failed. Reason: " + ex.Message +
          ". Item is not restored and is not removed from the queue.", Application.ProductName,
           MessageBoxButtons.OK, MessageBoxIcon.Error);
        ret = false;
      }

      return ret;
    }
    private bool RestoreLink(ExecInfo ei)
    {
      bool ret = false;

      try
      {
        ShellShortcut s = new ShellShortcut(ei.Source);
        s.Arguments = ei.Arguments;
        s.Path = ei.FullPath;
        s.WorkingDirectory = ei.WorkingDirectory;
        s.WindowStyle = ei.WindowStyle;
        s.Save();

        ret = true;
      }
      catch (Exception ex)
      {
        MessageBox.Show(
          "Creation of the link file " + ei.Source + " failed. Reason: " + ex.Message +
          ". Item is not restored and is not removed from the queue.", Application.ProductName,
           MessageBoxButtons.OK, MessageBoxIcon.Error);

        ret = false;
      }

      return ret;
    }

    private void btnRevert_Click(object sender, EventArgs e)
    {
      bool succesFlag = false;
      ExecInfo ei = q.Items[grd.CurrentCell.RowIndex];

      if (ei.FullPath.StartsWith("HK"))
      {
        succesFlag = RestoreRegistry(ei);
      }
      else
      {
        succesFlag = RestoreLink(ei);
      }

      if (succesFlag)
        q.Items.Remove(ei);
    }

    private void btnAbout_Click(object sender, EventArgs e)
    {
      MessageBox.Show(
        Application.ProductName +
        Environment.NewLine +
        Application.ProductVersion.ToString() +
          Environment.NewLine +
          Application.CompanyName +
          Environment.NewLine +
          "http://engstartqueue.codeplex.com/",
          Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
    }

    private void btnCreateBackup_Click(object sender, EventArgs e)
    {
      try
      {
        string path = null;

        Program.DoBackup(out path);

        MessageBox.Show("Backup succesfully created into " + path,
          Application.ProductName,  MessageBoxButtons.OK, MessageBoxIcon.Information);
      }
      catch (Exception ex)
      {
        MessageBox.Show("Failed to create backup. Reason: " + ex.Message,
          Application.ProductName,  MessageBoxButtons.OK, MessageBoxIcon.Error);
      }
    }

    private void FrmManager_FormClosing(object sender, FormClosingEventArgs e)
    {
      if (tmrSave.Enabled)
      {
        if (DialogResult.Yes == MessageBox.Show("You did not save some changes into queue file. " +
          "Saves can be changed by pressing \"Save\" button (blinking image of disket). Would you like to abort quit and save changes?" 
          + " If no, all changes will be lost.",
          Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Question))
          e.Cancel = true;
      }
    }

    bool isSaveIcon = false;
    private void tmrSave_Tick(object sender, EventArgs e)
    {
      if (isSaveIcon)
        btnSave.Image = Properties.Resources.ico_Save2;
      else
        btnSave.Image = Properties.Resources.ico_Save;
      isSaveIcon = !isSaveIcon;
    }

    private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
    {
      btnInsert.Enabled =
        (tabControl1.SelectedIndex != 3);
      btnReloadLists.Enabled = btnInsert.Enabled;
    }

    private void btnReloadLists_Click(object sender, EventArgs e)
    {
      FillLists();
    }

    private void btnOpenBackupFolder_Click(object sender, EventArgs e)
    {
      Process p = new Process();
      ProcessStartInfo psi = new ProcessStartInfo(Program.BackupPath);

      p.StartInfo = psi;

      p.Start();
    }

    private void btnOpenAppDataFolder_Click(object sender, EventArgs e)
    {
      Process p = new Process();
      ProcessStartInfo psi = new ProcessStartInfo(Program.ApplicationPath);

      p.StartInfo = psi;

      p.Start();
    }

  }
}
